package jsr292.java.lang.invoke;

import jsr292.weaver.Debug;
import jsr292.weaver.opt.OptimizerContext;
import jsr292.weaver.opt.OptimizerSnippets;
import jsr292.weaver.opt.OptimizerVarAllocator;

class MHFilter extends MethodHandle {
    private final MethodHandle target;
    private final MethodHandle[] filters;
    
    public MHFilter(MethodHandle target, MethodHandle[] filters, MethodType type) {
	super(type);
	this.target = target;
	this.filters = filters;
    }

    // no invoke with zero argument
    
    @Override
    public Object $invoke$(Object o1) throws Throwable {
	MethodHandle filter1 = filters[0];
        return target.$invoke$(
        	(filter1 ==null)?o1:filter1.$invoke$(o1));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
	MethodHandle filter3 = filters[2];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2),
        	(filter3 == null)?o3:filter3.$invoke$(o3));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
	MethodHandle filter3 = filters[2];
	MethodHandle filter4 = filters[3];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2),
        	(filter3 == null)?o3:filter3.$invoke$(o3),
        	(filter4 == null)?o4:filter4.$invoke$(o4));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
	MethodHandle filter3 = filters[2];
	MethodHandle filter4 = filters[3];
	MethodHandle filter5 = filters[4];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2),
        	(filter3 == null)?o3:filter3.$invoke$(o3),
        	(filter4 == null)?o4:filter4.$invoke$(o4),
        	(filter5 == null)?o5:filter5.$invoke$(o5));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
	MethodHandle filter3 = filters[2];
	MethodHandle filter4 = filters[3];
	MethodHandle filter5 = filters[4];
	MethodHandle filter6 = filters[5];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2),
        	(filter3 == null)?o3:filter3.$invoke$(o3),
        	(filter4 == null)?o4:filter4.$invoke$(o4),
        	(filter5 == null)?o5:filter5.$invoke$(o5),
        	(filter6 == null)?o6:filter6.$invoke$(o6));
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) throws Throwable {
        MethodHandle[] filters = this.filters;
	MethodHandle filter1 = filters[0];
	MethodHandle filter2 = filters[1];
	MethodHandle filter3 = filters[2];
	MethodHandle filter4 = filters[3];
	MethodHandle filter5 = filters[4];
	MethodHandle filter6 = filters[5];
	MethodHandle filter7 = filters[6];
        return target.$invoke$(
        	(filter1 == null)?o1:filter1.$invoke$(o1),
        	(filter2 == null)?o2:filter2.$invoke$(o2),
        	(filter3 == null)?o3:filter3.$invoke$(o3),
        	(filter4 == null)?o4:filter4.$invoke$(o4),
        	(filter5 == null)?o5:filter5.$invoke$(o5),
        	(filter6 == null)?o6:filter6.$invoke$(o6),
        	(filter7 == null)?o7:filter7.$invoke$(o7));
    }
    
    @Override
    public Object $invoke$(Object... rest) throws Throwable {
        int length = rest.length;
        MethodHandle[] filters = this.filters;
	for(int i=0; i<length; i++) {
	    MethodHandle filter = filters[i];
	    if (filter != null) {
                rest[i] = filter.$invoke$(rest[i]);
	    }
        }
        return target.$invoke$(rest);
    }
    
    @Override
    public boolean isWeavable(Class<?> classBeingRedefined) {
	for(MethodHandle filter:filters) {
	    if (filter == null)
		continue;
	    if (!filter.isWeavable(classBeingRedefined)) {
	        return false;
	    }
	}
	return target.isWeavable(classBeingRedefined);
    }
    
    @Override
    public void weave(OptimizerContext context, OptimizerVarAllocator allocator) {
        if (Debug.DEBUG) {
            Debug.checkAllocator(allocator, this);
        }
        
	OptimizerSnippets.filter(context, allocator, target, filters);
    }
}
